package pr.lanmu;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import pr.lanmu.annotation.PermissionIgnore;
import pr.lanmu.common.mapper.PermissionMapper;
import pr.lanmu.common.po.Permission;
import pr.lanmu.config.apijson.FunctionParser;
import pr.lanmu.config.apijson.SqlParser;
import pr.lanmu.config.apijson.Verifier;
import pr.lanmu.config.util.Log;

import javax.script.ScriptEngine;
import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@SpringBootApplication
@EnableScheduling
@SuppressWarnings("all")
@MapperScan(basePackages = {"pr.lanmu.common.mapper", "pr.lanmu.dao"})
public class AppApplication implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    /**
     * 文件上传保存路径
     */
    public static final String FILE_PATH = System.getProperty("user.dir");
    /**
     * 文件上传保存路径
     */
    public static final String TEMP_PATH = FILE_PATH + File.separator + "temp" + File.separator;
    /**
     * 链路时间存储
     */
    public static final ThreadLocal<ConcurrentHashMap<String, Long>> linkTracingTimes = new ThreadLocal<>();
    /**
     * JS引擎储存
     */
    public static final ThreadLocal<ScriptEngine> scriptEngine = new ThreadLocal<>();
    /**
     * 全局spring工具
     */
    public static ConfigurableApplicationContext app;
    /**
     * 全局线程池
     */
    public static ExecutorService pool = Executors.newFixedThreadPool(100, new NamedThreadFactory());
    /**
     * 保存自己的代理对象
     */
    private static AppApplication that;
    /**
     * 权限操作service
     */
    @Autowired
    private PermissionMapper permissionMapper;
    @Value("${server.port}")
    private Integer port;

    @SneakyThrows
    public static void main(String[] args) {
        //设置时区
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
        //禁用polyglot警告
        System.setProperty("polyglot.engine.WarnInterpreterOnly", "false");
        //启动springboot
        app = SpringApplication.run(AppApplication.class, args);
        //apiJson 启动debug模式
        apijson.Log.DEBUG = false;
        //apiJson 设置分页最大值
        SqlParser.DEFAULT_QUERY_COUNT = 99999;
        SqlParser.MAX_QUERY_PAGE = 99999;
        SqlParser.MAX_QUERY_COUNT = 99999;
        SqlParser.MAX_UPDATE_COUNT = 9999;
        SqlParser.MAX_SQL_COUNT = 400;
        SqlParser.MAX_OBJECT_COUNT = 9999;
        SqlParser.MAX_ARRAY_COUNT = 9999;
        SqlParser.MAX_QUERY_DEPTH = 20;
        //关闭自带的校验
        Verifier.ENABLE_VERIFY_ROLE = false;
        Verifier.ENABLE_VERIFY_CONTENT = false;
        FunctionParser.ENABLE_REMOTE_FUNCTION = false;
        pool.submit(() -> that.input());
    }

    /**
     * 根据代理对象找被代理对象
     *
     * @param obj 代理对象
     * @return 被代理对象
     */
    public static Object getTrueTargetFrom(Object obj) {
        try {
            Field field = obj.getClass()
                    .getDeclaredField("CGLIB$CALLBACK_1");
            field.setAccessible(true);
            MethodInterceptor interceptor = (MethodInterceptor) field.get(obj);
            Field advised = interceptor.getClass()
                    .getDeclaredField("target");
            advised.setAccessible(true);
            return advised.get(interceptor);
        } catch (NoSuchFieldException ignored) {
        } catch (IllegalAccessException e) {
            Log.error("获取代理对象失败", e);
        }
        return obj;
    }

    /**
     * 写入接口使用代理对象，否则找不到代理属性
     */
    @PostConstruct
    public void init() {
        that = this;
    }

    /**
     * 接口信息录入中
     */
    public void input() {
        Log.info("接口信息录入中...");
        List<Permission> allPermission = permissionMapper.selectAll();
        Map<String, Permission> groupMap = new ConcurrentHashMap<>();
        Map<String, Permission> permissionMap = new ConcurrentHashMap<>();
        allPermission.forEach(e -> {
            if (e.getPid() == 0) {
                groupMap.put(e.getUrl(), e);
            } else {
                permissionMap.put(e.getUrl(), e);
            }
        });
        Map<String, ?> beans = app.getBeansWithAnnotation(Tag.class);
        //筛选需要入库的接口
        List<Class<?>> clazzList = beans.values()
                .parallelStream()
                .map(val -> {
                    Object obj = getTrueTargetFrom(val);
                    Class<?> clazz = obj.getClass();
                    Tag api = clazz.getDeclaredAnnotation(Tag.class);
                    if (Objects.nonNull(api) && Objects.nonNull(api.name())) return clazz;
                    return null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        //接口收集，异步录入，忽略可能的错误，错误重启即可，且概率低
        clazzList.parallelStream()
                .forEach(clazz -> {
                    //添加接口组
                    Tag api = clazz.getDeclaredAnnotation(Tag.class);
                    Permission groupPermission;
                    RequestMapping requestMapping = clazz.getDeclaredAnnotation(RequestMapping.class);
                    Permission group = groupMap.remove(requestMapping.value()[0]);
                    if (Objects.nonNull(group)) {
                        groupPermission = group;
                    } else {
                        groupPermission = new Permission();
                        groupPermission.setPid(0L);
                        Optional.ofNullable(clazz.getDeclaredAnnotation(RequestMapping.class))
                                .ifPresent(e -> groupPermission.setUrl(e.value()[0]));
                        String name = api.name();
                        groupPermission.setName(name);
                        groupPermission.setIsLowCode(false);
                        permissionMapper.insertSelective(groupPermission);
                    }
                    Long pid = groupPermission.getId();
                    //添加接口
                    Arrays.stream(clazz.getDeclaredMethods())
                            .parallel()
                            .forEach(method -> {
                                Operation apiOperation = method.getDeclaredAnnotation(Operation.class);
                                PermissionIgnore permissionIgnore = method.getDeclaredAnnotation(PermissionIgnore.class);
                                if (Objects.nonNull(apiOperation)) {
                                    String value = apiOperation.summary();
                                    if (Objects.nonNull(value)) {
                                        Permission permissionPermission = new Permission();
                                        permissionPermission.setPid(pid);
                                        permissionPermission.setName(value);
                                        permissionPermission.setIsLowCode(false);
                                        if (permissionIgnore != null) permissionPermission.setOpen(true);
                                        permissionPermission.setController(clazz.getName());
                                        permissionPermission.setMethod(method.getName());
                                        Optional.ofNullable(method.getDeclaredAnnotation(PostMapping.class))
                                                .ifPresent(e -> permissionPermission.setUrl((groupPermission.getUrl() + "/" + e.value()[0]).replace("//", "/")));
                                        Optional.ofNullable(permissionMap.remove(permissionPermission.getUrl()))
                                                .ifPresent(e -> {
                                                    permissionPermission.setName(value);
                                                    permissionPermission.setId(e.getId());
                                                    permissionPermission.setPid(e.getPid());
                                                });
                                        permissionMapper.insertOrUpdate(permissionPermission, true);
                                    }
                                }
                            });
                });
        //删除代码中已经删除的接口组
        permissionMapper.deleteBatchByIds(groupMap.values()
                .parallelStream()
                .filter(e -> !e.getIsLowCode())
                .map(Permission::getId)
                .collect(Collectors.toList()));
        //删除代码中已经删除的接口
        permissionMapper.deleteBatchByIds(permissionMap.values()
                .parallelStream()
                .filter(e -> !e.getIsLowCode())
                .map(Permission::getId)
                .collect(Collectors.toList()));
    }

    /**
     * 外部访问http客户端配置
     *
     * @return .
     */
    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(150000);
        factory.setConnectTimeout(150000);
        return new RestTemplate(factory);
    }

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(port);
    }

    /**
     * 自定义线程池自定义线程名称
     */
    static class NamedThreadFactory implements ThreadFactory {
        private static final AtomicInteger tag = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("自定义线程池：" + tag.getAndIncrement());
            return thread;
        }
    }
}
